package com.csc.service;

import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.csc.constants.Constants;
import com.csc.dao.TransactionDao;
import com.csc.domain.Account;
import com.csc.domain.Transaction;
import com.csc.domain.User;

@Service
public class TransactionServiceImpl implements TransactionService {
	final static Logger logger = Logger.getLogger(TransactionServiceImpl.class);
	@Autowired
	TransactionDao transactionDao;
	@Autowired
	AccountService accountService;

	public List<Transaction> getTransactionListByAccountId(int userId) {
		return transactionDao.getTransactionListByAccountId(userId);
	}

	public Transaction findTransactionById(int transactionId) {
		return transactionDao.findTransactionById(transactionId);
	}

	public List<Transaction> getTransactionListByCustomer(Date from, Date to,
			int userId) {
		return transactionDao.getTransactionListByCustomer(from, to, userId);
	}

	public List<Transaction> getTransactionListPending() {
		return transactionDao.getTransactionListPending();
	}

	public boolean insertNewTransaction(Transaction newTransaction) {
		return transactionDao.insertNewTransaction(newTransaction);
	}

	public Transaction findTransactionByStringId(String transactionId) {
		return transactionDao.findTransactionByStringId(transactionId);
	}	

	public boolean updateTransaction(Transaction transaction) {
		return transactionDao.updateTransaction(transaction);
	}

	public boolean update(Account account, Transaction transaction) {
		return transactionDao.update(account, transaction);		
	}

	public boolean update(Account sendAccount, Account receiveAccount,
			Transaction transaction) {
		return transactionDao.update(sendAccount, receiveAccount, transaction);
	}

	/**
	 * TrangNguyen - Verify transaction will approve/reject
	 * 
	 * + Verify parseInt transactionId 
	 * + Verify transaction exist
	 * + Verify transaction approved	
	 * + Verify transaction rejected
	 * + Verify user support apply this transaction - now still has role a supporter	
	 * + Verify amount transfer - must greater than 0
	 * + Verify add funds
	 * + Verify withdraw
	 * + Verify transfer
	 */
	public String verifyTransaction(String transactionId) {
		
		logger.info("Verify transaction id = " + transactionId);
		
		// Verify parseInt transactionId 
		try {
			Integer.parseInt(transactionId);
		} catch (Exception e) {			
			return Constants.ERROR_PARSE_INT;
		}
		
		// get Transaction by transactionID
		Transaction transaction =  transactionDao.findTransactionById(Integer.parseInt(transactionId));
		
		// Verify transaction exist
		if (transaction == null ) {		
			return Constants.ERROR_TRANSACTION_NOT_FOUND;
		}
		
		int countError = 0;
		StringBuffer error = new StringBuffer();
		String transactionState = transaction.getTransactionStatus().trim();
		String transactionType = transaction.getTransactionType().trim();

		
		// Verify transaction type
		if (!transactionType.equals(Constants.TRANSACTION_TYPE_ADDFUNDS)
				&& !transactionType.equals(Constants.TRANSACTION_TYPE_WITHDRAW)
				&& !transactionType.equals(Constants.TRANSACTION_TYPE_TRANSFER)) {			
			error.append(Constants.ERROR_TRANSACTION_TYPE); countError++;			
		}
		
		// Verify transaction state		
		if (!transactionState.equals(Constants.TRANSACTION_STATE_PENDING)
				&& !transactionState.equals(Constants.TRANSACTION_STATE_APPROVED)
				&& !transactionState.equals(Constants.TRANSACTION_STATE_REJECTED)) {			
			error.append(Constants.ERROR_TRANSACTION_STATE); countError++;
		}
		
		// Verify transaction approved		
		if (transactionState.equals(Constants.TRANSACTION_STATE_APPROVED)) {							
			error.append(Constants.ERROR_TRANSACTION_APPROVED); countError++;
		}
		
		// Verify transaction rejected
		if (transactionState.equals(Constants.TRANSACTION_STATE_REJECTED)) {							
			error.append(Constants.ERROR_TRANSACTION_REJECTED); countError++;
		}
		
		// Verify user support who apply transaction - still has role a supporter		
		if ((int)transaction.getUserSupportId().getIdRole().getIdRole() != 1) {
			error.append(Constants.ERROR_USER_SUPPORT_NOT_ROLE); countError++;
		}
		
		// Verify transaction create date time		
		if (transaction.getBeginTime().after(new Date())) {
			error.append(Constants.ERROR_DATE_TIME_CREATED); countError++;
		}
		
		// Verify amount transfer - amount transfer must greater than 0 		
		if (transaction.getChangeAmount() < 1) {
			error.append(Constants.ERROR_AMOUNT_TRANSFER); countError++;
		}
		
		// Verify add funds
		if (transactionType.equals(Constants.TRANSACTION_TYPE_ADDFUNDS)) {
			// Verify add funds account active
			if (transaction.getSendAccountId().getIdState().getIdState() != 1) {
				error.append(Constants.ERROR_ADDFUNDS_ACCOUNT_NOT_ACTIVE); countError++;
			}
		} // Verify withdraw		
		else if (transactionType.equals(Constants.TRANSACTION_TYPE_WITHDRAW)) {
			// Verify withdraw account balance
			if (transaction.getSendAccountId().getBalance() < transaction.getChangeAmount()) {
				error.append(Constants.ERROR_BALANCE_FOR_WITHDRAW); countError++;
			}
			// Verify withdraw account active
			if (transaction.getSendAccountId().getIdState().getIdState() != 1) {
				error.append(Constants.ERROR_WITHDRAW_ACCOUNT_NOT_ACTIVE); countError++;
			}
		} // Verify transfer		
		else if (transactionType.equals(Constants.TRANSACTION_TYPE_TRANSFER)) {
			// Verify sender account balance
			if (transaction.getSendAccountId().getBalance() < transaction.getChangeAmount()) {
				error.append(Constants.ERROR_BALANCE_FOR_TRANSFER); countError++;
			}						
			// Verify sender account active
			if (transaction.getSendAccountId().getIdState().getIdState() != 1) {
				error.append(Constants.ERROR_SENDER_ACCOUNT_NOT_ACTIVE); countError++;
			}			
			// Verify receiver founded
			if (transaction.getReceiveAccountId() == null){
				error.append(Constants.ERROR_RECEIVER_ACCOUNT_NOT_FOUND); countError++;
			} else {
				// Verify sender account active
				if (transaction.getReceiveAccountId().getIdState().getIdState() != 1) {
					error.append(Constants.ERROR_RECEIVER_ACCOUNT_NOT_ACTIVE); countError++;
				}
				
				// Verify same sender and receiver 
				if (transaction.getSendAccountId().getAccountId() == transaction.getReceiveAccountId().getAccountId()) {
					error.append(Constants.ERROR_SAME_ACCOUNT_TRANSFER); countError++;		
				}
			}			
			
		}
		
		logger.info("Verify transaction completed, " + countError + " error founded");
		
		return error.toString();
	}

	// Approve transaction
	public boolean approve(String transactionId, User userAmin, String comment) {
		boolean check = false;
		Transaction trans = transactionDao.findTransactionById(Integer.parseInt(transactionId));
		if (trans.getTransactionType().equals(Constants.TRANSACTION_TYPE_ADDFUNDS)) {
			check = approveAddFunds(trans, userAmin, comment);
		} else if (trans.getTransactionType().equals(Constants.TRANSACTION_TYPE_WITHDRAW)) {
			check = approveWithdraw(trans, userAmin,comment);
		} else if (trans.getTransactionType().equals(Constants.TRANSACTION_TYPE_TRANSFER)) {
			check = approveTransfer(trans, userAmin,comment);
		} 
		return check;
	}


	// Reject transaction
	public boolean reject(String transactionId, User userAmin, String comment) {		
		try {
			Transaction trans = transactionDao.findTransactionById(Integer.parseInt(transactionId));
			trans.setEndTime(new Date());
			trans.setComment(comment);
			trans.setUserAdminId(userAmin);
			trans.setTransactionStatus(Constants.TRANSACTION_STATE_REJECTED);
			transactionDao.updateTransaction(trans);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	// Approve transaction type add funds
	public boolean approveAddFunds(Transaction transaction, User userAmin, String comment) {
		try {
			double currentBalance = transaction.getSendAccountId().getBalance();
			transaction.getSendAccountId().setBalance(currentBalance + transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdminId(userAmin);
			transaction.setTransactionStatus(Constants.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccountId(), transaction);
			
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	// Approve transaction type withdraw
	public boolean approveWithdraw(Transaction transaction, User userAmin, String comment) {
		try {
			double currentBalance = transaction.getSendAccountId().getBalance();
			transaction.getSendAccountId().setBalance(currentBalance - transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdminId(userAmin);
			transaction.setTransactionStatus(Constants.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccountId(), transaction);
			
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	// Approve transaction type transfer
	public boolean approveTransfer(Transaction transaction, User userAmin, String comment) {
		try {
			double currentBalanceSender = transaction.getSendAccountId().getBalance();			
			transaction.getSendAccountId().setBalance(currentBalanceSender - transaction.getChangeAmount());
			
			double currentBalanceReceiver = transaction.getReceiveAccountId().getBalance();
			transaction.getReceiveAccountId().setBalance(currentBalanceReceiver + transaction.getChangeAmount());
			transaction.setEndTime(new Date());
			transaction.setComment(comment);
			transaction.setUserAdminId(userAmin);
			transaction.setTransactionStatus(Constants.TRANSACTION_STATE_APPROVED);
			transactionDao.update(transaction.getSendAccountId(), transaction.getReceiveAccountId(), transaction);			
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	// create add funds transaction in database
	public boolean createAddFundsTransaction(double doubleAmount,
			Account tempAccount, User userSupport) {
		
		// crate transaction
		Transaction transferTransaction = new Transaction(new Date(),
				doubleAmount, Constants.TRANSACTION_TYPE_ADDFUNDS,
				Constants.TRANSACTION_STATE_PENDING, tempAccount, null, null,
				userSupport);
		
		// insert transaction in database
		try {
			transactionDao.insertNewTransaction(transferTransaction);
		} catch (Exception e) {
			return false;
		}
		
		return true;
	}

	// create withdraw transaction
	public boolean createWithdrawTransaction(double doubleAmount,
			Account tempAccount, User userSupport) {
		
		// create transaction
		Transaction transferTransaction = new Transaction(new Date(),
				doubleAmount, Constants.TRANSACTION_TYPE_WITHDRAW,
				Constants.TRANSACTION_STATE_PENDING, tempAccount, null, null,
				userSupport);
		
		// insert transaction in database
		try {
			transactionDao.insertNewTransaction(transferTransaction);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	// Create transfer transaction
	public boolean createTransferTransaction(String amountTransfer,
			String senderAccountNumber, String receiverAccountNumber,
			User userSupport) {

		try {
			// Sender account
			Account senderAccount = accountService.findAccountInfoByAccountInfoNumber(senderAccountNumber);
			// Receiver account
			Account receiverAccount = accountService.findAccountInfoByAccountInfoNumber(receiverAccountNumber);
			// New transfer transaction account
			Transaction transferTransaction = new Transaction(new Date(),
					Double.parseDouble(amountTransfer),
					Constants.TRANSACTION_TYPE_TRANSFER,
					Constants.TRANSACTION_STATE_PENDING, senderAccount,
					receiverAccount, userSupport);
			// insert transction in database
			transactionDao.insertNewTransaction(transferTransaction);	
		} catch (Exception e) {
			return false;
		}		

	return true;
	}

	public List<Transaction> getTransactionListComplete() {
		return transactionDao.getTransactionListComplete();
	}


	
	

}
